package cn.seaboot.admin.images.service;

import cn.seaboot.admin.images.bean.SysImage;
import cn.seaboot.admin.images.dao.SysImageDao;
import cn.seaboot.admin.starter.upload.UploadProperties;
import cn.seaboot.admin.upload.LocalStorage;
import cn.seaboot.admin.upload.OutputInfo;
import cn.seaboot.admin.upload.Uploader;
import cn.seaboot.admin.user.context.UserContext;
import cn.seaboot.commons.core.Asserts;
import cn.seaboot.commons.core.Snowflake;
import cn.seaboot.commons.core.SnowflakeFactory;
import cn.seaboot.commons.core.StringUtils;
import cn.seaboot.commons.exception.BizException;
import cn.seaboot.commons.file.FileUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * 系统图片管理
 * <p>
 * 图片是比较特殊的文件，对比于一般的文件，普通文件注重于业务的完整性，
 * <p>
 * 而图片更加注重于访问效率，图片查看过程中，在不需要鉴权的场合下，
 * 不需要查询数据库中存储的图片信息，可以直接访问图片。
 *
 * @author postm
 */
@Service
public class SysImageService {

    private final Snowflake snowflake = SnowflakeFactory.createSnowflake();

    @Resource
    private SysImageDao sysImageDao;

    @Resource
    private UserContext userContext;

    private Uploader uploader;

    @Resource
    private UploadProperties uploadProperties;

    /**
     * 初始化
     *
     * @throws IOException -
     */
    @PostConstruct
    public void init() throws IOException {
        File root = FileUtils.directory(uploadProperties.getImagePath());
        LocalStorage storage = new LocalStorage(root);
        uploader = new Uploader(storage);
    }

    /**
     * 保存图片
     *
     * @param file 文件实体
     * @param type 图片类型，主要用于区分业务，方便资源管理
     * @return SysImage
     * @throws IOException 磁盘操作异常
     */
    public SysImage upload(Integer type, @NotNull MultipartFile file) throws IOException {
        OutputInfo info = uploader.write(file);

        String realName = file.getOriginalFilename();
        Asserts.notNull(realName, "filename is null");

        SysImage image = new SysImage();
        image.setId(snowflake.nextId());

        image.setImageType(type);
        image.setImagePath(info.getPath());
        image.setUserId(userContext.getUserId());
        image.setImageName(StringUtils.truncateHead(realName, 32));

        sysImageDao.insert(image);
        return image;
    }

    /**
     * 按ID删除
     *
     * @param id ID
     * @return affected rows
     */
    @Transactional(rollbackFor = Exception.class)
    public int deleteById(Long id) throws IOException {
        SysImage image = sysImageDao.selectById(id);
        if (image == null) {
            throw new FileNotFoundException("file not found: " + id);
        } else if (Boolean.TRUE.equals(image.getLock())) {
            throw new BizException("file is locked!");
        } else {
            int ret = sysImageDao.deleteById(image.getId());
            uploader.delete(image.getImagePath());
            return ret;
        }
    }

//    /**
//     * 设计上，是可以跳过数据库查询，直接打开文件输入流的，只要业务表存储了文件的相对路径
//     *
//     * @param id 相对路径
//     * @throws IOException -
//     */
//    public File getFile(Long id) throws IOException {
//        SysImage img = imageDao.selectById(id);
//        if (img == null) {
//            throw new FileNotFoundException("file not found: " + id);
//        }
//        return uploader.getFile(img.getImagePath());
//    }

    /**
     * 打开文件输入流
     *
     * @throws IOException -
     */
    public InputStream openInputStream(String path) throws IOException {
        return uploader.openInputStream(path);
    }

    /**
     * 根据当前登录账号查询图片列表
     *
     * @param userId 用户 ID
     * @return 图片列表
     */
    public List<SysImage> queryList(String userId) {
        return sysImageDao.selectList(userId);
    }

    /**
     * 按ID查询
     *
     * @param id ID
     * @return pojo
     */
    public SysImage queryById(Long id) {
        return sysImageDao.selectById(id);
    }

    /**
     * 锁定文件，避免文件删除等操作
     *
     * @param id ID
     * @return affected rows
     */
    public int lock(Long id) {
        return sysImageDao.setLock(id, true);
    }

    /**
     * 文件解锁，避免文件删除等操作
     *
     * @param id ID
     * @return affected rows
     */
    public int unLock(Long id) {
        return sysImageDao.setLock(id, false);
    }
}
